<?php
require_once(dirname(__FILE__).'/html.php');
require_once(dirname(__FILE__).'/sandbox.php');
allow('text|urlstrip|nicetime|richtext|url|base_url|no_cache|truncate|text|texts|wording|label|translation|language_id|href|encoding|authhref');

function encoding($value = NULL) {
	static $encoding = 'UTF-8';
	if (func_num_args() > 0) $encoding = $value;
	return $encoding;
}

/**
 * @desc Receive a link by its controller name
 * @param name Module name in the form: "controller.action"
 * @param ... Parameters to include in the URL
 */
function href() {
}

/**
 * @desc Receive a link that will automatically authenticate the logged in user before navigating to a given URL ("deep link")
 * @param name Module name in the form: "controller.action"
 * @param ... Parameters to include in the URL
 */
function authhref() {
}

function redirect($url = NULL) {
	if (!isset($url)) $url = current_url();
	session_write_close();
	@header("Location: $url", true, 302);
	if (!function_exists('headers_sent') || headers_sent()) {
		$meta = '<meta http-equiv=refresh content="0;URL='.htmlentities($url).'"/>';
		htmlhead($meta);
		echo "<html><head>$meta</head></html>";
	}
	exit;
}

function route($url, &$params = NULL) {
}

/**
 * @desc Return an internationalized text
 * @param ... The first parameter may be a label 
 */
function text(/*...*/) {
	$args = func_get_args();
	$vars = 0;

	// Find the number of variables by iterating searching all arguments for the maximum number 
	for ($i = 0; $i < count($args)-$vars; $i++) $vars = max($vars, preg_match_all("~(?:^|[^%])%[^%]~", $args[$i], $match));
	$text = call_user_func_array("label", $args);
	$vars = array_splice($args, max(0, count($args)-$vars));
	$label = array_shift($args);
	while (!isset($text) && count($args)) $text = array_pop($args);

	// Enable the %n variable for grammatical alternations:
	// For example, a formatted string "0 sheeps|1 sheep|%n sheeps" will result either in '0 sheeps', '1 sheep' but '174 sheeps'
	if (($pos = strpos($text, '%n')) !== false) {
		$texts = explode('|', $text);
		$offset = preg_match('~(?<!%)%[^%]~', substr($text, 0, $pos));
		$default = $pos ? substr_count($text, '|', 0, $pos) : 0;
		$num = $vars[$offset];
		if ($num < count($texts)) $text = $texts[$num];
		else $text = $texts[$default];
		$text = str_replace('%n', $num, $text);
	}
	array_unshift($vars, $text);
	return call_user_func_array("sprintf", $vars);
}
// TODO: important: reduce DB queries and load all labels at once
function texts(/*...*/) {
	$texts = array();
	for ($i = 0, $l = func_num_args(); $i < $l; $i++) {
		$arg = func_get_arg($i);
		$texts[$arg] = text($arg);
	}
	return $texts;
}
function wording(&$arg) {
	$args = array_slice(func_get_args(), 1);
	$args[] = &$arg;
	return call_user_func_array('text', $args);
}
function label($name, $default = NULL) {
	static $labels = array();
	$args = func_get_args();
	$name = array_shift($args);
	while (!isset($default) && count($args)) $default = array_shift($args);
	if (empty($name) || substr_count($name, '%')) return $name;

	if (array_key_exists($name, $labels)) $text = $labels[$name];
	return $labels[$name] = cache("translation", $name, language_id(), OEM, $default);
}

/**
 * @desc Cleanly truncate a string by adding "..." if necessary
 */
function truncate($str, $len) {
	$tail = max(0, $len-10);
	$trunk = substr($str, 0, $tail);
	substr($str, $tail, $len);
	$trunk .= strrev(preg_replace('~^..+?[\s,:]\b|^...~', '...', strrev(substr($str, $tail, $len-$tail))));
	return $trunk;
}

/**
 * @desc Add slashes to special characters and escape line breaks
 */
function json_quote($str) {
  return preg_replace("~[\\0- ]~", " ", preg_replace("~\r?\n~", "\\n", addslashes($str)));
  //return str_replace("\n", "\\n", addslashes($str));
}

/**
 * @desc Filter last output level's buffer when a fatal error occurs: ob_start('ob_error_handler')
 */
function ob_error_handler($str) {
	$error = error_get_last();
	if ($error && ($error["type"] == E_USER_ERROR || $error["type"] == E_ERROR)) {
		return ini_get("error_prepend_string").
		  "\nFatal error: $error[message] in $error[file] on line $error[line]\n".
		  ini_get("error_append_string");
	}
	return $str;
}

/**
 * @desc Silently include a PHP file and return its output as a string 
 */
function file_get_output($file, $vars = NULL) {
	for ($i = 1; $i < func_num_args(); $i++) {
		$vars = func_get_arg($i);
		@extract($vars, EXTR_SKIP);
	}
	ob_start("ob_error_handler");
	include($file);
	$data = ob_get_contents();
	ob_end_clean();
	return $data;
}

/**
 * @desc Locates an extension file containing for a requested symbol (i.e. method or function) and includes it
 */
function extension($name, $type = NULL) {
  static $loaded = array();
  $base = dirname(__FILE__);
  $str = strtr(str_replace('::', '_', $name), '_', '/');
  $offset = 0;
  do {
    $offset = strpos($str, '/', $offset+1);
    $path = $offset === false ? $str : substr($str, 0, $offset);

    $key = "$type $path";
    if (isset($loaded[$key])) continue;
    $loaded[$key] = true;

    if (file_exists("$base/$path.php")) require_once "$base/$path.php";
    if (isset($type) && file_exists("$base/$type/$path.php")) require_once "$base/$type/$path.php";
  } while ($offset !== false);
}

/**
 * @desc Freezes an open session. Any modifications of session variables will be discarded by session_unfreeze() or on exit. 
 */
function &session_freeze() {
	static $session;
	if (!isset($session) && strlen(session_id())) {
		$session = $_SESSION;
		register_shutdown_function('session_unfreeze');
	}
	return $session;
}
function session_unfreeze() {
	$session = &session_freeze();
	if ($session !== $_SESSION) {
		session_start();
		$_SESSION = $session;
	}
}

function &singleton($class) {
	static $instances = array();
	$class = strtolower($class);
	if (isset($instances[$class])) return $instances[$class];
	$instances[$class] = new $class;
	return $instances[$class];
}

function no_cache() {
  header("Expires: Tue, 03 Jul 2001 06:00:00 GMT");
  header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
  header("Cache-Control: post-check=0, pre-check=0", false);
  header("Pragma: no-cache");
}

/**
 * @desc Return the base URL of the server's virtual host excluding the trailing slash
 */
function base_url() {
	static $base_url;
	if (!isset($base_url)) {
		$base_url = ($_SERVER['HTTPS'] ? 'https' : 'http')."://$_SERVER[SERVER_NAME]";
		if ($_SERVER['SERVER_PORT'] != ($_SERVER['HTTPS'] ? 443 : 80)) $base_url .= ":$_SERVER[SERVER_PORT]";
	}
	return $base_url;
}

/**
 * @desc Return the full URL of the current HTTP request
 */
function current_url() {
	return base_url().$_SERVER['REQUEST_URI'];
}

/**
 * @desc Add a query parameter to a given URL
 */
function http_set_param($url, $name, $value) {
	if (strpos($url, '#') !== false) $url = strstr($url, '#', true);
	$query = strpos($url, '?');
	$name = urlencode($name);
	$value = urlencode($value);
	if ($query !== false) $url = substr($url, 0, $query+1).str_replace('&&', '&', preg_replace("~$name=[^&]*&?~", '', substr($url, $query+1)))."&$name=$value";
	else $url .= "?$name=$value";
	return $url;
}

/**
 * @desc Web-2.0-style relative dates
 */
function nicetime($date) {
	if(empty($date)) return "";

	$periods = array("second", "minute", "hour", "day", "week", "month", "year", "decade");
	$lengths = array(60, 60, 24, 7, 4.35, 12, 10);

	$now = time();
	$unix_date = is_numeric($date) ? intval($date) : strtotime($date);
	if(empty($unix_date)) return false;

	$past = $now > $unix_date;
	$difference = $past ? $now-$unix_date : $unix_date-$now;

	for($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) $difference /= $lengths[$j];

	$difference = round($difference);
	$text = $period = $periods[$j];
	if($difference != 1) $text .= 's';

	if ($past) $text = text("nicetime_$period".'ago', "%s $text ago", $difference);
	else $text = text("nicetime_$period".'from_now', "%s $text from now", $difference);
	return $text;
}
function richtext($text) {
	return $text;
}

/**
 * @desc Ommit or replace all special chars in a string by simple letters for the use in an URL path
 */
function urlstrip($str) {
	$tr_from = 'ŠŒŽšœžŸ¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ';
	$tr_to = 'SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy';
	$replace_from = array('ö', 'ä', 'ü', 'ß');
	$replace_to = array('oe', 'ae', 'ue', 'ss');
	// Has an effect when core.php is UTF-encoded
	$str = str_replace($replace_from, $replace_to, $str);
	$str = strtr($str, $tr_from, $tr_to);
    $str = strtolower(utf8_decode($str));
	// Has an effect when core.php file is ANSI-encoded
	$str = str_replace($replace_from, $replace_to, $str);
	$str = strtr($str, $tr_from, $tr_to);
	$str = preg_replace('~^(.{64}.{0,16}?)-.*|^(.{80}).*~', '$1$2', $str);
	return preg_replace('~^\-+|\-+$~', '', preg_replace('~[^a-z]+~', '-', $str));
}

/**
 * @desc Directory functions
 */
function document_root() {
	static $root;
	if (isset($root)) return $root;
	if (isset($_SERVER['DOCUMENT_ROOT']) && file_exists($_SERVER['DOCUMENT_ROOT'])) return $root = realpath($_SERVER['DOCUMENT_ROOT']);
	return $root = dirname(dirname(__FILE__));
}
function localpath($path) {
	// Strip ., .., and /
	$path = strtr($path, '\\', '/');
	// TODO: fix echo preg_replace('~^(/)?[./]*/~', '\1','/ssjk/kak', 1);
	$path = preg_replace('~^(/)?[./]+/~', '\1', preg_replace('~.*/\.*/~', '', preg_replace('~/\.*[^/.][^/]*/\.\.(?:/|$)~', '/\1', preg_replace('~/.(?=/|$)~', '', $path))));
	// Make absolute directory relative
	if (is_absolute($path)) {
		$root = strtr(document_root(), '\\', '/');
		$offset = strlen($root);
		if (substr_compare($root, $path, $offset, true) == 0 && $path[$offset] == '/') {
			$path = substr($path, $offset+1);
		}
	}
	return $path;
}
function serverpath($path = NULL) {
	return document_root().(isset($path) ? '/'.localpath($path) : '');
}
function tmppath($path = '') {
	$path = sys_get_temp_dir()."/htdocs/$_SERVER[SERVER_NAME]/$path";
  	if (!is_dir($path)) mkdir($path, 0777, true);
  	return $path;
}
function is_absolute($str) {
	return $str[0] == '/' || $str[0] == '\\' || preg_match('~^\w+:~', $str);
}
if (!function_exists('sys_get_temp_dir')) {
	function sys_get_temp_dir() {
		static $dir;
		if (isset($dir)) return $dir;
		$root = document_root();
		$paths = array(dirname($root), $root, '');
		foreach ($paths as $path) if (is_dir("$path/tmp") && is_writable("$path/tmp")) return "$path/tmp";
		foreach ($paths as $path) if (mkdir("$path/tmp")) return "$path/tmp";
		return false;
	}
}

function preprocess($name /*, ...*/) {
	$projects = project(NULL);
	$args = func_get_args();
	$name = array_shift($args);
	foreach ($projects as $project) {
		if (class_exists($project) && method_exists($project, $name)) return forward_static_call_array(array($project, $name), $args);
		if (function_exists($project."_$name")) return call_user_func_array($project."_$name", $args);
	}
	if (function_exists($name)) return call_user_func_array($name, $args);
	return false;
}
function preprocess_array($name, $args) {
	array_unshift($args, $name);
	return call_user_func_array('call_project_func', $args);
}
